package cn.sdormitory.common.exception;

import cn.sdormitory.common.api.CommonResult;
import cn.sdormitory.common.api.ResultCode;
import cn.sdormitory.common.utils.ServletUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.ClientAbortException;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.RedisSystemException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * 异常处理器
 */
@Slf4j
@RestControllerAdvice
public class ApiExceptionHandler {

    /**
     * 处理自定义异常
     */
    @ExceptionHandler(ApiException.class)
    public CommonResult handleApiException(ApiException e) {
        log.error("自定义ApiException 抛出: {}",e);
        return CommonResult.failed(e.getCode(), e.getMsg());
    }

    @ExceptionHandler(value = {MissingServletRequestParameterException.class})
    public CommonResult<?> missingServletRequestParameterException(MissingServletRequestParameterException ex) {
        log.error("missing servlet request parameter exception:{}", ex.getMessage());
        return CommonResult.failed(ResultCode.VALIDATE_FAILED.getCode(),"缺少参数"+ex.getParameterName())
                .setHint(ex.getMessage());
    }

    @ExceptionHandler(value = {MissingServletRequestPartException.class})
    public CommonResult<?> missingServletRequestPartException(MissingServletRequestPartException ex) {
        log.error("missing servlet request part exception:{}", ex.getMessage());
        return CommonResult.failed(ResultCode.VALIDATE_FAILED.getCode(),"缺少参数"+ex.getRequestPartName())
                .setHint(ex.getMessage());
    }

    @ExceptionHandler(value = {ClientAbortException.class})
    public CommonResult<?> clientAbortException(ClientAbortException ex, HttpServletRequest request) {
        log.error("客户端已断开连接,请求地址:{}",request.getRequestURI());
        return CommonResult.failed(ResultCode.FAILED.getCode(),"客户端已断开连接").setHint(ex.getMessage());
    }

    @ExceptionHandler(value = {MultipartException.class})
    public CommonResult<?> uploadFileLimitException(@RequestPart(value = "file") MultipartException ex) {
        log.error("upload file size limit:{}", ex.getMessage());
        return CommonResult.failed(ResultCode.UPLOAD_FILE_SIZE_FAILED).setHint(ex.getMessage());
    }

    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    public CommonResult<?> serviceException(MethodArgumentNotValidException ex) {
        log.error("service exception:{}", ex.getMessage());
        StringBuilder errorMsgBuilder=new StringBuilder();
        List<ObjectError> allErrors = ex.getBindingResult().getAllErrors();
        for(ObjectError allError:allErrors){
            errorMsgBuilder.append(allError.getDefaultMessage()).append(";");
        }
        errorMsgBuilder.deleteCharAt(errorMsgBuilder.length() - 1);
        log.error(errorMsgBuilder.toString());
        String errorMsg=allErrors.get(0).getDefaultMessage();
        return CommonResult.failed(ResultCode.VALIDATE_FAILED.getCode(),errorMsg).setHint(ex.getMessage());
    }

    @ExceptionHandler(value = {DuplicateKeyException.class})
    public CommonResult<?> duplicateKeyException(DuplicateKeyException ex) {
        log.error("primary key duplication exception:{}", ex.getMessage());
        return CommonResult.failed(ResultCode.DUPLICATE_PRIMARY_KEY).setHint(ex.getMessage());
    }

    @ExceptionHandler(RedisConnectionFailureException.class)
    public CommonResult<String> handleRedisConnectionFailureException(Exception e) {
        log.error("连不上redis服务器",e);
        return CommonResult.<String>failed("连接redis失败").setHint(e.getMessage());
    }

    @ExceptionHandler(RedisSystemException.class)
    public CommonResult<String> handleRedisSystemExceptionException(Exception e) {
        log.error(ExceptionUtils.getFullStackTrace(e));
        String message = e.getMessage();
        if(message == null || !message.endsWith("NOAUTH Authentication required.")){
            message = "redis操作失败";
        }else {
            message = "redis授权错误";
        }
        CommonResult<String> result = CommonResult.failed(message);
        return result.setHint(e.getMessage());
    }

    @ExceptionHandler(value = {IllegalArgumentException.class})
    public CommonResult<String> handleIllegalArgumentException(Exception e) {
        log.error("参数错误 {}",e.getMessage());
        CommonResult<String> result = CommonResult.failed(e.getMessage());
        return result.setHint(e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public CommonResult handleException(Exception e) {
        log.error(e.getMessage(), e);
        return CommonResult.failed();
    }

    private String getRequestUri() {
        return ServletUtils.getRequestOptional().map(HttpServletRequest::getRequestURI).orElse("无");
    }
}
